#include <map>
#include <sstream>
#include <assert.h>
#include <iostream>
#include <unistd.h>
#include <arpa/inet.h>

#include "parser.h"
#include "tty_io.hpp"
#include "helper.hpp"
#include "interface.hpp"
#include "fingerprint.hpp"
#include "config_parser.hpp"

extern std::map<unsigned int, Base*> gids;
extern std::map<unsigned int, Mode> modes;
extern std::map<unsigned int, Panel> panels;
extern std::map<unsigned int, Sensor> sensors;
extern std::map<unsigned int, Command> commands;
extern std::map<unsigned int, Timer> user_timers;
extern std::map<unsigned int, Serial_info> port_map;

/* GSM class implementation */
void Gsm::send_msg() 
{
    char *msg;
    char resp[256];
    std::string msg_num;

    msg_num = "AT+CMGS=\"" + this->number + "\"";

    msg = (char*)malloc(msg_num.size() + 2);
    strcpy(msg, msg_num.c_str());
    write_to_gsm(msg);
    free(msg);

    usleep(200000);
    read_from_gsm(resp);

    msg = (char*)malloc(this->text.size() + 2);
    strcpy(msg, this->text.c_str());
    logger(SM_LOG_INFO,"sending msg = %s\n", msg);

    for(size_t i = 0; i < strlen(msg); ++i){
        if(msg[i] == '^') {
            msg[i] = 10;
        }
    }

    write_to_gsm(msg);
    free(msg);
    usleep(300000);
    read_from_gsm(resp);

    char ctrl_z[4];
    ctrl_z[0] = 0x1A;
    ctrl_z[1] = 0;

    /* finish the msg */
    write_to_gsm(ctrl_z);
}

void Gsm::set(std::string v) 
{
    send_msg();
}

std::string Gsm::get() 
{
    return "gsm";
}

void Sensor::set(std::string v)
{
    pthread_mutex_lock(&this->set_get_mutex);

    int current_value = atoi(this->value.c_str());
    int new_value = atoi(v.c_str());

    if(current_value != new_value) {
        if(this->attribute) {
            this->attribute->set_value(v);
            save_config();
        }

        // if this->ivent_handler == 0 then no ivent_hanler was specified.
        if(this->ivent_handler != 0) {
            std::map<unsigned int, Base*>::iterator b_it;
            b_it = gids.find(this->ivent_handler);
            if(b_it == gids.end()){
                logger(SM_LOG_ERR,"wrong 'gid' specified in the ivent_handler gid = [g%d]\n", this->ivent_handler);
                exit(-1);
            }
            gids[this->ivent_handler]->set(v);
        }

        this->value = v;
    }
    
    pthread_mutex_unlock(&this->set_get_mutex);
}

std::string Sensor::get()
{   
    std::string v;
    pthread_mutex_lock(&this->set_get_mutex);
    v = this->value;
    pthread_mutex_unlock(&this->set_get_mutex);
    return v;
}

/* Base Class implementation used only for Var types. */
Base::Base()
{ 
    this->attribute = NULL;
    pthread_mutex_init(&this->set_get_mutex, NULL); 
}
void Base::set(std::string v)
{
    pthread_mutex_lock(&this->set_get_mutex);
    // check if we are in the virtual mode.
    if(v[0] == '#') 
        this->value = v.substr(1);
    else
        this->value = v;

    if(this->attribute) {
        this->attribute->set_value(v);
        save_config();
    }
    pthread_mutex_unlock(&this->set_get_mutex);
}

std::string Base::get()
{   
    std::string v;
    pthread_mutex_lock(&this->set_get_mutex);
    v = this->value;
    pthread_mutex_unlock(&this->set_get_mutex);
    return v;
}

/* Panel implementation */
void Panel::start_timer()
{
    timer_settime(this->timerid, 0, &this->timeout, NULL);
}

void Panel::init()
{   
    b_packet_t *req;
    p_packet_t *resp;

    int retry_count = 10;
    req = str_to_pkt(this->init_data);

retry:
    retry_count--;
    resp = (p_packet_t*)send_request(this->port, req, this->read_timeout);

    if(!resp) {
        logger(SM_LOG_ERR,"ERROR INITING PANEL: = retrying, count = %d...\n", (10 - retry_count));
        if(retry_count == 0) {
            free(req);
            return;
        }
        usleep(100000);
        goto retry;
    }

    std::string pkt_str = pkt_to_str((b_packet_t*)resp);

    for(size_t i = 0; i < this->sensors.size(); i++) {
        float v = -1;
        switch(this->sensors[i]->type) {
            case CONTACT: 
                v = get_contact(resp, this->sensors[i]->id);
                break;
            case BUTTON: 
                v = get_button(resp, this->sensors[i]->id);
                break;
            case DIGITAL: 
                v = get_digital(resp, this->sensors[i]->id);
                if(v == -1) {
                    v = atof(this->sensors[i]->get().c_str());
                }
                break;
            default:
                break;
        }
        this->sensors[i]->value = to_string(v);
        logger(SM_LOG_INFO,"INIT sensor->g%d:[%.02f], packet_dump = [%s]\n", 
                this->sensors[i]->gid, v, pkt_str.c_str());
    }
    free(req);
    free(resp);
}

void* Panel::update_routine(void* obj)
{   
    b_packet_t *req;
    p_packet_t *resp;
    Panel *p = (Panel*)obj;
    struct itimerspec curr_value;

    pthread_detach(pthread_self());
    req = str_to_pkt(p->update_data);
    p->start_timer();
    for(;;){
        timer_gettime(p->timerid, &curr_value);
        if((!curr_value.it_value.tv_sec && !curr_value.it_value.tv_nsec)){
            p->start_timer();
        } else {
            nanosleep(&curr_value.it_value, NULL);
            continue;
        }

        resp = (p_packet_t*)send_request(p->port, req, p->read_timeout);

        if(!resp) {
            continue;
        }

        std::string pkt_str = pkt_to_str((b_packet_t*)resp);
        for(size_t i = 0; i < p->sensors.size(); i++) {
            float v = 0;
            switch(p->sensors[i]->type) {
                case CONTACT: 
                    v = get_contact(resp, p->sensors[i]->id);
                    break;
                case BUTTON: 
                    v = get_button(resp, p->sensors[i]->id);
                    break;
                case DIGITAL: 
                    v = get_digital(resp, p->sensors[i]->id);
                    if(v == -1) {
                        v = atof(p->sensors[i]->get().c_str());
                    }
                    break;
            }
            int current_value = atoi(p->sensors[i]->get().c_str());
            int new_value = atoi(to_string(v).c_str());
            if (current_value != new_value){
                p->sensors[i]->set(to_string(v));
                logger(SM_LOG_INFO,"update sensor->g%d:[%.02f], packet_dump = [%s]\n", 
                        p->sensors[i]->gid, v, pkt_str.c_str());
            }
        }
        free(resp);
    }
    free(req);
    pthread_exit(NULL);
}

void Panel::set(std::string v)
{
    logger(SM_LOG_ERR,"panel don't have any value to be set.\n");
}

std::string Panel::get()
{
    logger(SM_LOG_ERR,"Panel hasn't any value yet.\n");
    return "Error: panel hasn't any value."; 
}

void Panel::update()
{
    pthread_create(&this->thread, NULL, &Panel::update_routine,
            static_cast<void*>(this));
}

/* Command Class implementation */

Command::Command()
{
    // default read timeout;
    mode = FAST;
    read_timeout.tv_sec = 0;
    read_timeout.tv_usec = 50000;

    pthread_mutex_init(&this->set_get_mutex, NULL);
}
void Command::set(std::string v)
{
    // check if we are in the virtual mode.
    if(v[0] == '#') {
        pthread_mutex_lock(&this->set_get_mutex);
        this->value = v.substr(1);
        logger(SM_LOG_INFO,"Virtually changed the [g%d] Command's value to %s\n",
                this->gid, this->value.c_str());
        pthread_mutex_unlock(&this->set_get_mutex);
        return;
    }

    pthread_mutex_lock(&this->set_get_mutex);
    b_packet_t *req, *resp;
    std::string data = this->data_for_set;

    find_and_replace(data, "value", v);

    /* find and replace g* string with it's value. */
    replace_ids_to_values(this->data_gids, data);

    req = str_to_pkt(data);
    resp = send_request(this->port, req, this->read_timeout);

    if(!resp) {
        free(req);
        pthread_mutex_unlock(&this->set_get_mutex);
        return;
    }

    free(req);
    free(resp);

    this->value = v;

    for(size_t i = 0; i < this->connections.size(); i++) {
        gids[this->connections[i].first]->set(string("#") +
                gids[this->connections[i].second]->get());
    }

    //logger(SM_LOG_INFO,"SET--->[g%d] = %s\n", this->gid, v.c_str());
    pthread_mutex_unlock(&this->set_get_mutex);
}

std::string Command::get()
{   
    pthread_mutex_lock(&this->set_get_mutex);
    std::string v = "0";
    if(this->mode == SLOW) {
        uint32_t tmp_value = 0;
        b_packet_t *req, *resp;

        req = str_to_pkt(this->data_for_get);
        resp = send_request(this->port, req, this->read_timeout);

        if(!resp) {
            free(req);
            logger(SM_LOG_ERR,"Can't read response from the device, returning '-1'\n");
            pthread_mutex_unlock(&this->set_get_mutex);
            return "-1";
        }

        assert((int)(this->start_byte + this->bytes_to_get - 2) < (int)(resp->size + 3));

        for(int i = 0; i < this->bytes_to_get; ++i) {
            tmp_value |= *((uint8_t*)resp + (this->start_byte + i - 1)) << i*8;
        }

        free(req);
        free(resp);

        v = to_string(tmp_value);
    } else {
        v = this->value;
    }

    pthread_mutex_unlock(&this->set_get_mutex);

    return v;
}

void Command::init()
{   
    uint32_t value = 0;
    int retry_count = 10;
    b_packet_t *req, *resp;

    this->value = "0";
    req = str_to_pkt(this->data_for_get);

retry:
    retry_count--;
    resp = send_request(this->port, req, this->read_timeout);

    if(!resp) {
        logger(SM_LOG_ERR,"ERROR INITING COMMAND: = retrying, count = %d...\n", (10 - retry_count));
        if(retry_count == 0) {
            free(req);
            return;
        }
        usleep(100000);
        goto retry;
    }

    assert((this->start_byte + this->bytes_to_get - 2) < resp->size + 3);

    for(int i = 0; i < this->bytes_to_get; ++i) {
        value |= *((uint8_t*)resp + (this->start_byte + i - 1)) << i*8;
    }

    free(req);
    free(resp);

    this->value = to_string(value);
    logger(SM_LOG_INFO,"INIT [%s]:%d\n", this->name.c_str(), (int)value);
}

/* Condition class implementation */
Condition::Condition()
{
    /* sets the default timer to 0.0 sec for the checking the condition. */
    this->sevp.sigev_notify = SIGEV_NONE;
    this->step_sevp.sigev_notify = SIGEV_NONE;

    timer_create(CLOCK_REALTIME, &this->sevp, &this->timerid);
    timer_create(CLOCK_REALTIME, &this->step_sevp, &this->step_timerid);

    timeout.it_value.tv_sec = 0;
    timeout.it_value.tv_nsec = 0;
    timeout.it_interval.tv_sec = 0;
    timeout.it_interval.tv_nsec = 0;

    step_timeout.it_value.tv_sec = 0;
    step_timeout.it_value.tv_nsec = 0;
    step_timeout.it_interval.tv_sec = 0;
    step_timeout.it_interval.tv_nsec = 0;
}

void* Condition::start_routine(void* obj)
{
    Parser eval;
    int eval_result;
    Condition* c = (Condition*)obj;
    std::string eval_expr = c->expr;
    std::map<unsigned int, Base*>::iterator b_it;

    pthread_detach(pthread_self());
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    pthread_mutex_lock(&c->m->working_mutex);
    c->m->working_count++;
    pthread_mutex_unlock(&c->m->working_mutex);

    /* Extracting 'g*' global IDs from the expression */
    std::vector<unsigned int> gids_str;
    extract_gids(c->expr, gids_str);

    bool timeout_specified = false;
    bool step_timeout_specified = false;
    bool forever = true;

    if(c->step_timeout.it_value.tv_sec != 0 || c->step_timeout.it_value.tv_nsec != 0){
        step_timeout_specified = true;
        c->start_timer("step_timeout");
    }

    if(c->timeout.it_value.tv_sec || c->timeout.it_value.tv_nsec){
        timeout_specified = true;
        c->start_timer("timeout");
    }

    if(!timeout_specified && !step_timeout_specified){
        forever = false;
    }

    do {
        /* Reset the expression string and start over. */
        eval_expr = c->expr;

        if(step_timeout_specified){
            struct itimerspec curr_value;
            timer_gettime(c->step_timerid, &curr_value);

            if((!curr_value.it_value.tv_sec && !curr_value.it_value.tv_nsec)){
                c->start_timer("step_timeout");
            } else {
                pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
                nanosleep(&curr_value.it_value, NULL);
                pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
                continue;
            }
        }

        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

        /* find and replace g* string with it's value. */
        replace_ids_to_values(gids_str, eval_expr);

        find_and_replace(eval_expr, "value", c->m->get_value());
        find_and_replace(eval_expr, "off", "0");
        find_and_replace(eval_expr, "on", "1");

        replace_timers(eval_expr);

        eval_result = eval.parse(eval_expr.c_str());
        //logger(SM_LOG_INFO,"GID[%d]#Eval %s: %s Result: %d\n", c->m->gid,
                //c->expr.c_str(), eval_expr.c_str(), eval_result);

        if(eval_result == 1){
            if(timeout_specified){
                if(c->is_timeout(c->timerid)){
                    exec(c->true_cmds, *c);
                    c->start_timer("timeout");
                }
            } else {
                exec(c->true_cmds, *c);
            }
        } else {
            if(timeout_specified){
                /* reset_timeout */
                c->start_timer("timeout");
            }
            exec(c->false_cmds, *c);
        }

        if(!forever) {
            c->m->turn_off_mode();
        }
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    }
    while(forever);

    pthread_exit(NULL);
}

void Condition::start()
{   
    pthread_create(&this->thread, NULL, &Condition::start_routine,
            static_cast<void*>(this));
}

void Condition::stop()
{   
    //if(this->thread){
        pthread_cancel(this->thread);
    //}
}

bool Condition::is_timeout(timer_t &timerid)
{
    struct itimerspec curr_value;

    timer_gettime(timerid, &curr_value);
    return (!curr_value.it_value.tv_sec && !curr_value.it_value.tv_nsec);
}

void Condition::start_timer(std::string t_str)
{
    if(t_str == "step_timeout"){
        timer_settime(this->step_timerid, 0, &this->step_timeout, NULL);
    } else if(t_str == "timeout"){
        timer_settime(this->timerid, 0, &this->timeout, NULL);
    }
}

/* Mode class implementation */
Mode::Mode()
{ 
    this->attribute = NULL;
    pthread_mutex_init(&this->set_get_mutex, NULL);
    pthread_mutex_init(&this->v_mutex, NULL);
    pthread_mutex_init(&this->working_mutex, NULL);
}

void Mode::start()
{   
    std::map<unsigned int, Base*>::iterator b_it;

    /* Executing 'actions'. */
    for(size_t i = 0; i < this->actions.size(); i++) {
        b_it = gids.find(actions[i].first);
        if(b_it == gids.end()){
            logger(SM_LOG_ERR,"wrong 'gid' in the action, gid = [%d]\n", actions[i].first);
            exit(-1);
        }
        if(this->actions[i].first == this->gid){
            if(this->actions[i].second == "off"){
                logger(SM_LOG_INFO,"Turning 'off' Mode: [%d]\n", this->gid);
                this->state = "off";
                return;
            } else {
                std::string tmp = this->actions[i].second;
                if(tmp[0] == '('){
                    tmp = to_string(eval_expr(tmp, this->value));
                }
                this->set_value(tmp);
            }
        } else {
            std::string tmp = this->actions[i].second;
            if(tmp[0] == '('){
                tmp = to_string(eval_expr(tmp, this->value));
            }
            gids[this->actions[i].first]->set(tmp);
        }
    }

    if(this->cnds.size() == 0) {
        this->state = "off";
        logger(SM_LOG_INFO,"Turning 'off' Mode: [%d]\n", this->gid);
        return;
    }

    /* Starting all conditions via threads. */
    for(std::vector<Condition>::iterator it = this->cnds.begin();
            it < this->cnds.end(); ++it) {
        it->m = this;
        it->start();
    }
}

void Mode::stop()
{
    Condition* c = NULL;

    /* Stop all the conditions of this mode. */
    for(std::vector<Condition>::iterator it = this->cnds.begin(); 
            it < this->cnds.end(); ++it){
        /* determine if the stop routine was called from the same thread. */
        if(pthread_equal(it->thread, pthread_self())){
            /* save the current condition for stopping it later. */
            c = &(*it);
        } else {
            it->stop();
        }
    }

    this->working_count = 0;
    if(c){
        if(this->attribute) {
            this->attribute->set_value(this->state);
            save_config();
        }
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        pthread_mutex_unlock(&this->set_get_mutex);
        pthread_mutex_unlock(&this->working_mutex);
        pthread_exit(NULL);
    }
}

void Mode::set(std::string v)
{
    pthread_mutex_lock(&this->set_get_mutex);
    if(v != "off"){
        if(this->state == "off") {
            logger(SM_LOG_INFO,"Turning 'on' Mode: [%d], value=%s\n", this->gid, v.c_str());
            this->state = "on";
            if(v != "on"){
                this->set_value(v);
            }
            this->start();
        } else {
            if(this->get_value() != v){
                this->set_value(v);
                logger(SM_LOG_INFO,"the mode [%d] value is changed to '%s'\n",
                        this->gid, v.c_str());
            }
        }
    } else {
        if(this->state != "off") {
            logger(SM_LOG_INFO,"Turning 'off' Mode: [%d]\n", this->gid);
            this->state = "off";
            this->stop();
        } else {
            logger(SM_LOG_INFO,"the mode [%d] is already turned 'off'\n",
                    this->gid);
        }
    }
    if(this->attribute) {
        this->attribute->set_value(this->state);
        save_config();
    }
    pthread_mutex_unlock(&this->set_get_mutex);
}

void Mode::turn_off_mode()
{
    pthread_mutex_lock(&this->working_mutex);
    this->working_count--;
    if(this->working_count <= 0) {
        this->set("off");
        this->working_count = 0;
    }
    pthread_mutex_unlock(&this->working_mutex);
}

std::string Mode::get()
{
    std::string v;
    pthread_mutex_lock(&this->set_get_mutex);
    if(this->state == "on"){
        v = "1";
    } else {
        v = "0";
    }
    pthread_mutex_unlock(&this->set_get_mutex);

    return v;
}

void Mode::set_value(std::string v)
{
    pthread_mutex_lock(&this->v_mutex);
    this->value = v;
    pthread_mutex_unlock(&this->v_mutex);
}

std::string Mode::get_value()
{
    pthread_mutex_lock(&this->v_mutex);
    std::string v;
    v = this->value ;
    pthread_mutex_unlock(&this->v_mutex);
    return v;
}

/* Timer class implementation */
void* Timer::start_routine(void* obj)
{
    Timer* t = (Timer*)obj;
    double eval_result;

    pthread_detach(pthread_self());
    t->start_timer();
    logger(SM_LOG_INFO,"TIMER [%d] called with value = [%s]\n", t->gid, t->value.c_str());
    while(true) {
        if(!(t->value == "0")){
            struct itimerspec curr_value;
            timer_gettime(t->timerid, &curr_value);

            if((!curr_value.it_value.tv_sec && !curr_value.it_value.tv_nsec)){
                t->start_timer();
            } else {
                nanosleep(&curr_value.it_value, NULL);
                continue;
            }
        }

        logger(SM_LOG_INFO,"TIMER [%d] expired, calling actions\n", t->gid);
        pthread_mutex_lock(&t->t_mutex);
        std::vector< std::pair<unsigned int, std::string> > cmd = t->actions;
        for(size_t i = 0; i < cmd.size(); i++) {
            if(cmd[i].second[0] == '('){
                replace_timers(cmd[i].second);
                find_and_replace(cmd[i].second, "value", t->get_value());
                eval_result = eval_expr2(cmd[i].second);
                if(cmd[i].first == t->gid && cmd[i].second == "off"){
                    t->state = "off";
                    pthread_mutex_unlock(&t->t_mutex);
                    pthread_exit(NULL);
                }
                logger(SM_LOG_INFO,"cmd = %d, value = %02f\n", cmd[i].first, eval_result);
                gids[cmd[i].first]->set(to_string((int)eval_result));
            } else {
                if(cmd[i].first == t->gid && cmd[i].second == "off"){
                    t->state = "off";
                    pthread_mutex_unlock(&t->t_mutex);
                    pthread_exit(NULL);
                }
                gids[cmd[i].first]->set(cmd[i].second);
            }
        }
        if(!(t->value == "0")){
            printf("NEW TIMER [%d] called with value = [%s]\n", t->gid, t->value.c_str());
            t->start_timer();
        }
        pthread_mutex_unlock(&t->t_mutex);
    }

    pthread_exit(NULL);
}

void Timer::start()
{   
    pthread_create(&this->thread, NULL, &Timer::start_routine,
            static_cast<void*>(this));
}

void Timer::stop()
{   
    pthread_mutex_lock(&this->t_mutex);
    if(this->thread){
        pthread_cancel(this->thread);
    }
    pthread_mutex_unlock(&this->t_mutex);
}

void Timer::set(std::string v)
{
    pthread_mutex_lock(&this->set_get_mutex);
    if(v != "off"){
        if(this->state == "off") {
            logger(SM_LOG_INFO,"Turning 'on' Timer: [%d]\n", this->gid);
            this->state = "on";
            if(v != "on"){
                this->set_value(v);
            }
            this->start();
        } else {
            if(this->get_value() != v){
                this->set_value(v);
                logger(SM_LOG_INFO,"the timer's [%d] value is changed to '%s'\n",
                        this->gid, v.c_str());
            }
        }
    } else {
        if(this->state != "off") {
            logger(SM_LOG_INFO,"Turning 'off' Timer: [%d]\n", this->gid);
            this->state = "off";
            this->stop();
        } else {
            logger(SM_LOG_INFO,"the timer [%d] is already turned 'off'\n",
                    this->gid);
        }
    }
    pthread_mutex_unlock(&this->set_get_mutex);
}

std::string Timer::get()
{
    std::string v;
    pthread_mutex_lock(&this->set_get_mutex);
    if(this->state == "on"){
        v = "1";
    } else {
        v = "0";
    }
    pthread_mutex_unlock(&this->set_get_mutex);

    return v;
}

void Timer::set_value(std::string v)
{
    pthread_mutex_lock(&this->v_mutex);
    this->value = v;
    float f = atof(v.c_str());
    float fractional;
    float integral;
    fractional = modff(f, &integral);
    this->timeout.it_value.tv_sec = (int)integral;
    this->timeout.it_value.tv_nsec = (long)lrintf((fractional) * 1000) * 1000000;
    this->timeout.it_interval.tv_sec = 0;
    this->timeout.it_interval.tv_nsec = 0;
    pthread_mutex_unlock(&this->v_mutex);
}

std::string Timer::get_value()
{
    std::string v;
    pthread_mutex_lock(&this->v_mutex);
    v = this->value ;
    pthread_mutex_unlock(&this->v_mutex);
    return v;
}

bool Timer::is_timeout()
{
    struct itimerspec curr_value;

    timer_gettime(this->timerid, &curr_value);
    return (!curr_value.it_value.tv_sec && !curr_value.it_value.tv_nsec);
}

void Timer::start_timer()
{
    timer_settime(this->timerid, 0, &this->timeout, NULL);
}

void Fingerprint::set(std::string value)
{
    int v;
    pthread_mutex_lock(&this->set_get_mutex);
    switch(this->cmd_type) {
        case FP_ENROLL:
            v = fp_enroll();
            break;
        case FP_IDENTIFY:
            v = fp_identify();
            break;
        case FP_DELETE:
            v = fp_delete();
            break;
        case FP_CHANGE_LEVEL: 
            {
                std::vector<std::string> tokens;
                tokenize(value, " ", tokens);
                assert(tokens.size() == 2); // tokens[0] == id, tokens[1] == level;
                v = fp_change_level(atoi(tokens[0].c_str()), 
                        atoi(tokens[1].c_str()));
            }
            break;
        default:
            break;
    }
    this->value = to_string(v);
    pthread_mutex_unlock(&this->set_get_mutex);
}

std::string Fingerprint::get()
{
    std::string v;
    pthread_mutex_lock(&this->set_get_mutex);
    v = this->value;
    pthread_mutex_unlock(&this->set_get_mutex);
    return v;
}

std::string Function::get()
{
    return "NULL"; // TODO:
}

void Function::set(std::string value)
{
    Parser eval;
    int eval_result;
    std::string eval_expr = this->expr;
    std::map<unsigned int, Base*>::iterator b_it;

    logger(SM_LOG_INFO,"Function [g%d]: value=%s\n", this->gid, value.c_str());
    /* find and replace g* string with it's value. */
    replace_ids_to_values(this->expr_gids, eval_expr);

    find_and_replace(eval_expr, "value", value);
    find_and_replace(eval_expr, "off", "0");
    find_and_replace(eval_expr, "on", "1");

    replace_timers(eval_expr);

    eval_result = eval.parse(eval_expr.c_str());
    //logger(SM_LOG_INFO,"GID[%d]#Eval %s: %s Result: %d\n", c->m->gid,
    //c->expr.c_str(), eval_expr.c_str(), eval_result);

    if(eval_result == 1){
        exec2(this->true_cmds, value);
    } else {
        exec2(this->false_cmds, value);
    }
}

void SysSleep::set(std::string secs)
{
    double seconds = atof(secs.c_str());
    logger(SM_LOG_INFO,"Sleeping %.03f seconds ... \n", seconds);
    usleep((useconds_t)(seconds * 1000000));
    return;
}

std::string SysSleep::get()
{
    return "SLEEP";
}
